package clientyt

import (
	"gitee.com/zhancaihua/goyt/clientyt/middleware"
	"gitee.com/zhancaihua/goyt/core/discover"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
	"log"
	"time"
)

type (
	ClientMiddlewaresConf struct {
		Timeout     bool          `mapstructure:"timeout" json:"timeout,omitempty"`
		TimeoutTime time.Duration `mapstructure:"timeout_time" json:"timeout_time,omitempty"`
	}
	// A RpcClient is a rpc client.
	RpcClient struct {
		client Client
	}
	RpcClientConf struct {
		Etcd          discover.EtcdConf     `mapstructure:"etcd" json:"etcd"`
		Endpoints     []string              `mapstructure:"endpoints" json:"endpoints,omitempty"`
		NonBlock      bool                  `mapstructure:"non_block" json:"non_block,omitempty"`
		KeepaliveTime time.Duration         `mapstructure:"keepalive_time" json:"keepalive_time,omitempty"`
		Middlewares   ClientMiddlewaresConf `mapstructure:"middlewares" json:"middlewares"`
	}
)

// BuildTarget builds the rpc target from the given config.
func (cc RpcClientConf) BuildTarget() (string, error) {
	//如果制定了Target就无需使用etcd，可能是用户想直连或者其他需求
	if len(cc.Endpoints) > 0 {
		return discover.BuildDirectTarget(cc.Endpoints), nil
	}

	if err := cc.Etcd.Validate(); err != nil {
		return "", err
	}
	//如果需要etcd发现就把相关配置设置进去
	if cc.Etcd.HasAccount() {
		discover.AddAccount(cc.Etcd.Hosts, cc.Etcd.User, cc.Etcd.Pass)
	}

	return discover.BuildDiscovTarget(cc.Etcd.Hosts, cc.Etcd.Key), nil
}

// MustNewRpcClient returns a Client, exits on any error.
func MustNewRpcClient(c RpcClientConf, options ...ClientOption) Client {
	cli, err := NewRpcClient(c, options...)
	if err != nil {
		log.Fatal(err)
	}

	return cli
}

// NewRpcClient returns a Client.
func NewRpcClient(c RpcClientConf, options ...ClientOption) (Client, error) {
	var opts []ClientOption
	if c.NonBlock {
		opts = append(opts, WithNonBlock())
	}
	if c.KeepaliveTime > 0 {
		opts = append(opts, WithDialOption(grpc.WithKeepaliveParams(keepalive.ClientParameters{
			Time: c.KeepaliveTime,
		})))
	}

	opts = append(opts, options...)
	//中间件
	opts = append(opts, buildStreamInterceptors(c.Middlewares)...)
	opts = append(opts, buildUnaryInterceptors(c.Middlewares)...)
	target, err := c.BuildTarget()
	if err != nil {
		return nil, err
	}

	client, err := NewClient(target, opts...)
	if err != nil {
		return nil, err
	}

	return &RpcClient{
		client: client,
	}, nil
}

// Conn returns the underlying grpc.ClientConn.
func (rc *RpcClient) Conn() *grpc.ClientConn {
	return rc.client.Conn()
}
func buildStreamInterceptors(c ClientMiddlewaresConf) []ClientOption {
	var interceptors []ClientOption

	return interceptors
}

func buildUnaryInterceptors(c ClientMiddlewaresConf) []ClientOption {
	var interceptors []ClientOption

	if c.Timeout {
		interceptors = append(interceptors, WithUnaryClientInterceptor(middleware.TimeoutInterceptor(c.TimeoutTime)))
	}

	return interceptors
}
